/**
 *Copyright [2010] [lcj(caijunlu@gmail.com)]
 *Licensed under the Apache License, Version 2.0 (the "License");
 *you may not use this file except in compliance with the License. 
 *You may obtain a copy of the License at 
 *             http://www.apache.org/licenses/LICENSE-2.0 
 *Unless required by applicable law or agreed to in writing, 
 *software distributed under the License is distributed on an "AS IS" BASIS, 
 *WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 
 *either express or implied. See the License for the specific language governing permissions and limitations under the License
 */
package org.independent.servlet.session.util;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

import net.rubyeye.xmemcached.CASOperation;
import net.rubyeye.xmemcached.MemcachedClient;
import net.rubyeye.xmemcached.MemcachedClientBuilder;
import net.rubyeye.xmemcached.XMemcachedClientBuilder;
import net.rubyeye.xmemcached.exception.MemcachedException;
import net.rubyeye.xmemcached.transcoders.Transcoder;
import net.rubyeye.xmemcached.utils.AddrUtil;

import org.independent.servlet.session.exception.ServletSessionException;

/**
 * the client for memcahe;
 * 
 * @author lcj
 * 
 */
public class XMemcachedClient {
	/**
	 * Transcoder is an interface for classes that convert between byte arrays
	 * and objects for storage in the cache.
	 */
	static private Transcoder<Object> defaultTranscoder = null;
	private MemcachedClient mc = null;

	public MemcachedClient getMc() {
		return mc;
	}

	private static XMemcachedClient instance = null;
	private int memcachedSessionTime;

	public static XMemcachedClient getInstance(final String serverlist,
			final int memcachedSessionTime, final Transcoder<Object> transcoder) {
		synchronized (XMemcachedClient.class) {
			if (instance == null) {
				instance = new XMemcachedClient(serverlist,
						memcachedSessionTime, transcoder);
			}
		}
		return instance;
	}

	private XMemcachedClient(final String serverlist,
			final int memcachedSessionTime, Transcoder<Object> transcoder) {
		this.memcachedSessionTime = memcachedSessionTime;
		defaultTranscoder = transcoder;
		try {
			MemcachedClientBuilder builder = new XMemcachedClientBuilder(
					AddrUtil.getAddresses(serverlist), new int[] { 1, 3 });
			mc = builder.build();
			mc.setOpTimeout(10 * 1000L);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	final public boolean set(final String key, final Object obj) {
		try {
			return mc.set(key, memcachedSessionTime, obj);
		} catch (TimeoutException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (MemcachedException e) {
			e.printStackTrace();
		}
		return false;
	}

	final public boolean set(final String key, final Object obj,
			Transcoder<Object> transcoder) {
		try {
			if (transcoder != null) {
				return mc.set(key, memcachedSessionTime, obj, transcoder, -1);
			}
			return mc.set(key, memcachedSessionTime, obj);
		} catch (TimeoutException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (MemcachedException e) {
			e.printStackTrace();
		}
		return false;
	}

	final public boolean defaultSet(final String key, final Object obj) {
		try {
			if (defaultTranscoder != null) {
				return mc.set(key, memcachedSessionTime, obj,
						defaultTranscoder, 99999);
			}
			return mc.set(key, memcachedSessionTime, obj);
		} catch (TimeoutException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (MemcachedException e) {
			e.printStackTrace();
		}
		return false;
	}

	final public Object get(final String key) throws Exception {
		try {
			return mc.get(key);
		} catch (Exception e) {
			throw e;
		}
	}

	final public Object get(final String key, final Transcoder<?> transcoder)
			throws Exception {
		try {
			if (transcoder != null) {
				return mc.get(key, transcoder);
			}
			return mc.get(key);
		} catch (Exception e) {
			throw e;
		}
	}

	final public Object defaultGet(final String key) throws Exception {
		try {
			if (defaultTranscoder != null) {
				return mc.get(key, defaultTranscoder);
			}
			return mc.get(key);
		} catch (Exception e) {
			throw e;
		}
	}

	final public boolean add(final String key, final Object obj) {
		try {
			return mc.add(key, memcachedSessionTime, obj);
		} catch (Exception e) {
			dealException(e);
			return false;
		}
	}

	final public boolean add(final String key, final Object obj,
			final Transcoder<Object> transcoder) {
		try {
			if (transcoder != null) {
				return mc.add(key, memcachedSessionTime, obj, transcoder, -1);
			}
			return mc.add(key, memcachedSessionTime, obj);
		} catch (Exception e) {
			dealException(e);
			return false;
		}
	}

	final public boolean defaultAdd(final String key, final Object obj) {
		try {
			if (defaultTranscoder != null) {
				return mc.add(key, memcachedSessionTime, obj,
						defaultTranscoder, -1);
			}
			return mc.add(key, memcachedSessionTime, obj);
		} catch (Exception e) {
			dealException(e);
			return false;
		}
	}

	final public boolean replace(final String key, final Object obj) {
		try {
			return mc.replace(key, memcachedSessionTime, obj);
		} catch (Exception e) {
			dealException(e);
			return false;
		}
	}

	final public boolean replace(final String key, final Object obj,
			final Transcoder<Object> transcoder) {
		try {
			if (transcoder != null) {
				return mc.replace(key, memcachedSessionTime, obj, transcoder,
						-1);
			}
			return mc.replace(key, memcachedSessionTime, obj);
		} catch (Exception e) {
			dealException(e);
			return false;
		}
	}

	final public boolean defaultReplace(final String key, final Object obj) {
		try {
			if (defaultTranscoder != null) {
				return mc.replace(key, memcachedSessionTime, obj,
						defaultTranscoder, -1);
			}
			return mc.replace(key, memcachedSessionTime, obj);
		} catch (Exception e) {
			dealException(e);
			return false;
		}
	}

	final public boolean cas(final String key, final int expire,
			final CASOperation<?> oper) {
		try {
			return this.mc.cas(key, expire, oper);
		} catch (Exception e) {
			dealException(e);
			return false;
		}
	}

	final public boolean cas(final String key, final int expire,
			final CASOperation<?> oper, final Transcoder transcoder) {
		try {
			if (transcoder != null) {
				return mc.cas(key, expire, oper, transcoder);
			}
			return this.mc.cas(key, expire, oper);
		} catch (Exception e) {
			dealException(e);
			return false;
		}
	}

	final public boolean defaultCas(final String key, final int expire,
			final CASOperation<Object> oper) {
		try {
			if (defaultTranscoder != null) {
				return mc.cas(key, expire, oper, defaultTranscoder);
			}
			return this.mc.cas(key, expire, oper);
		} catch (Exception e) {
			dealException(e);
			return false;
		}
	}

	final public boolean delete(final String key) {
		try {
			return this.mc.delete(key);
		} catch (Exception e) {
			dealException(e);
			return false;
		}
	}

	private void dealException(Exception exp) {
		try {
			throw exp;
		} catch (TimeoutException e) {
			throw new ServletSessionException("XMemcachedClient time out !", e);
		} catch (InterruptedException e) {
			throw new ServletSessionException(" XMemcachedClient Interrupt!", e);
		} catch (MemcachedException e) {
			throw new ServletSessionException("Memcached exception happened!",
					e);
		} catch (Exception e) {
			e.printStackTrace();
			throw new ServletSessionException(
					"XMemcachedClient other exception!", e);
		}
	}
}
